﻿using FlyingEye.ApplicationContracts;
using FlyingEye.ApplicationContracts.SignalRHubs;
using FlyingEye.SignalRHubs;
using log4net;
using MaterialTypeRecognition.Shell.SaveInfos;
using MaterialTypeRecognition.Shell.SaveInfoService;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using WebMiddleware.SaveInfoService;
using WebMiddleware.Store;

namespace FlyingEyeClient.ApplicationServices.SignalRHubs
{
    public class SignalRClientService
    {
        private HubConnection? _connection;

        public HubConnection Connection
        {
            get
            {
                if (_connection == null || _connection.State != HubConnectionState.Connected)
                {
                    throw new InvalidOperationException("未连接 Hub 服务器！");
                }

                return _connection;
            }
        }

        private readonly ILog _log;

        public bool IsConnected => _connection?.State == HubConnectionState.Connected;

        public string ClientId { get; }

        public SignalRClientService(string clientId, ILog log)
        {
            _log = log;
            ClientId = clientId;
        }

        /// <summary>
        /// 连接事务回调
        /// </summary>
        public event EventHandler<ConnectionCallbackEventArgs>? ConnectionCallback;

        public event EventHandler<ClassifyMeterialMessage>? ClassifyMeterial;

        /// <summary>
        /// 连接 Hub 服务器
        /// </summary>
        /// <exception cref="InvalidOperationException">注册节点失败异常</exception>
        public async Task ConnectAsync()
        {
            ISaveInfoService service = new TypeSaveInfoService(new TypeSaveInfoStore());
            var saveInfo = service.Read<AppSettingSaveInfo>();

            _connection = new HubConnectionBuilder()
            .WithUrl(new Uri($"https://{saveInfo.SignalRHost}/DeviceClientHub"))
            .WithAutomaticReconnect(new SignalRRetryPolicy())
            .Build();

            try
            {
                // 连接到 HUB 中心
                await _connection.StartAsync();
            }
            catch (Exception ex)
            {
                _log.Error("连接 HUB 中心错误", ex);
                ConnectionCallback?.Invoke(this, new ConnectionCallbackEventArgs("连接 HUB 中心失败！"));

                // 这里是连接失败，释放资源即可
                await _connection.DisposeAsync();
                _connection = null;
                return;
            }

            // 添加客户端映射
            await _connection.InvokeAsync("AddClientMappingAsync", new ClientMappingMessage() { DeviceId = this.ClientId });

            _connection.Reconnecting += OnReconnecting;
            _connection.Reconnected += OnReconnected;

            // 注册方法
            RegisterMethods(_connection);
        }

        private Task OnReconnecting(Exception? arg)
        {
            this.ConnectionCallback?.Invoke(this, new ConnectionCallbackEventArgs("正在重新连接 Hub 服务器.."));

            return Task.CompletedTask;
        }

        private async Task OnReconnected(string? arg)
        {
            // 重新连接完成之后，重新注册客户端
            if (_connection != null)
                // 添加客户端映射
                await _connection.InvokeAsync("AddClientMappingAsync", new ClientMappingMessage() { DeviceId = this.ClientId });
        }

        /// <summary>
        /// 注册 API 方法，然后我们可以从服务器调用这些方法
        /// </summary>
        private void RegisterMethods(HubConnection hubConnection)
        {
            hubConnection.On<ClassifyMeterialMessage>("ExecuteClassifyMeterialAsync", message =>
            {
                ClassifyMeterial?.Invoke(this, message);
            });
        }

        /// <summary>
        /// 断开连接。
        /// </summary>
        public async Task DisconnectAsync()
        {
            // 关闭 SignalR 连接
            if (_connection != null)
            {
                await _connection.StopAsync();
                _connection = null;
            }
        }
    }
}
